#!/usr/bin/env python
# -*- coding: UTF-8 -*-

import logging
import os
import re
import shutil
import tempfile
from oriolus.pith.archivers.factory import PacketExtractorFactory
from oriolus.pith.packets.metareader import FileMetaReader
from oriolus.pith.packets.scriptreader import ScriptReader
from oriolus.pith.packets.script import Script

class Status:
    NEW = "new"
    BAD = "bad"
    INSTALLING = "installing"
    INSTALLED = "installed"
    INSTALLING_AS_DEPENDENCY = "installing_as_dependency"
    INSTALLED_AS_DEPENDENCY = "installed_as_dependency"
    REMOVED = "removed"
    ALL = "all"
    
    status_map = {INSTALLING:INSTALLED, INSTALLING_AS_DEPENDENCY:INSTALLED_AS_DEPENDENCY}

class FullNameParseError(Exception):
    def __init__(self, message):
        Exception.__init__(self, message)

class FullName:
    def __init__(self, name, version):
        self.__name = name
        self.__version = version

    def parse(cls, full_name_string):
        pattern = re.compile("^(.+)-(\d+\.\d+\.\d+\.\d+)$")
        result = pattern.search(full_name_string)

        if not result:
            raise FullNameParseError("'%s' in not a packet full name" % full_name_string)
        
        return FullName(result.group(1), result.group(2)) 

    parse = classmethod(parse)

    @property
    def name(self):
        return self.__name 

    @property
    def version(self):
        return self.__version

    def __str__(self):
        return "%s-%s" % (self.__name, self.__version)

    def __eq__(self, other):
        if not (type(other) is type(self)):
            return False

        if self.__name != other.__name:
            return False

        if self.__version != other.__version:
            return False

        return True
    
    def __ne__(self, other):
        return not self.__eq__(other)

    def __hash__(self):
        return hash(str(self))    

class Packet:
    def __init__(self, meta, scripts=None, status=Status.NEW, basepath=None):
        if scripts is None:
            scripts = {}
        self.__meta = meta
        self.__basepath = basepath
        self.__status = status
        self.__scripts = scripts

    @property
    def name(self):
        return str(FullName(self.__meta.name, self.__meta.version))

    @property
    def meta(self):
        return self.__meta

    def get_status(self):
        return self.__status

    def set_status(self, status):
        self.__status = status

    status = property(get_status, set_status)

    @property
    def scripts(self):
        return self.__scripts
    
    @property
    def basepath(self):
        return self.__basepath
    

class FilePacket(Packet):
    def __init__(self, packet_path, base_path = None, status=Status.NEW, packet_extractor_factory=PacketExtractorFactory(),
        meta_reader=FileMetaReader(), script_reader=ScriptReader()):
        self._is_disposed = True
        
        self.__logger = logging.getLogger("FilePacket")
        
        self.__packet_path = packet_path
        self.__temp_dir = base_path
        if self.__temp_dir == None:
            self.__temp_dir = tempfile.mkdtemp()
            
        if not os.path.exists(self.__temp_dir):
            os.makedirs(self.__temp_dir)
            
        packet_extractor = packet_extractor_factory.get_extractor(packet_path)
        self.__logger.info("Extracting packet '" + packet_path + "' to '" + self.__temp_dir + "'")
        packet_extractor.extract(packet_path, self.__temp_dir)
        meta = meta_reader.read(os.path.join(self.__temp_dir , "META"))
        scripts = script_reader.read(os.path.join(self.__temp_dir , "scripts"))
        Packet.__init__(self, meta, scripts, status, base_path)

        self._is_disposed = False
        
    def dispose(self):
        try:
            if self.basepath not in [None, ""] and os.path.exists(self.basepath):
                self.__safe_remove(os.path.join(self.basepath, "META"))
                self.__safe_remove(os.path.join(self.basepath, "CHECKSUMS"))
                self.__safe_remove(os.path.join(self.basepath, os.path.basename(self.__packet_path)))
                self.__safe_remove_dir(os.path.join(self.basepath, "scripts"))
                self.__safe_remove_dir(os.path.join(self.basepath, "data")) 
                self.__safe_remove(os.path.join(self.basepath, "data"))              
            elif os.path.exists(self.__temp_dir):
                shutil.rmtree(self.__temp_dir)
        finally:    
            self._is_disposed = True
        
    def __safe_remove(self, path):
        if os.path.exists(path):
            os.remove(path)
            
    def  __safe_remove_dir(self, path):
        if os.path.exists(path) and os.path.isdir(path):
            shutil.rmtree(path)
        
    def __del__(self):
        if not self._is_disposed:
            self.dispose()
            
class RemovePacket(Packet):
    def __init__(self, source_packet):
        scripts = source_packet.scripts
        
        if source_packet.basepath not in [None, ""]:
            scripts = self.__copy_packet_scripts(source_packet) 

        Packet.__init__(self, source_packet.meta, scripts, source_packet.status, source_packet.basepath)
    
    def __copy_packet_scripts(self, source_packet):
        scripts_path = os.path.join(source_packet.basepath, "scripts")
        if not os.path.exists(scripts_path):
            os.mkdir(scripts_path)
        
        [shutil.copy(script.path, os.path.join(scripts_path, os.path.basename(script.path))) for script in source_packet.scripts]
       
        return [Script(script.name ,os.path.join(scripts_path, os.path.basename(script.path))) for script in source_packet.scripts]        